#include "tansu_internal.h"
#include "tansu_socket.h"
#include "tansu_nwif.h"
#include "tansu_util.h"
#include "tansu_http.h"
#include "tansu_ssdp.h"
#include "tansu_upnp.h"
#include "tdom.h"
#include "tdom_writer.h"


void
tansu_device_entry_soap_exec(struct evhttp_request *req,
                             struct tansu_device *device,
                             struct tansu_service *service,
                             const char *action,
                             void *arg)
{
    struct evbuffer *buffer;
    struct tdom_doc *result = NULL;

    printf("[action] %s\n", action);
    buffer = evbuffer_new();

    if (strcmp(action, "GetSystemUpdateID") == 0) {
        result = tdom_soap_new("urn:schemas-upnp-org:service:ContentDirectory:1", "GetSystemUpdateIDResponse");
        tdom_soap_set(result, "Id", "1");
    } else if (strcmp(action, "GetSortCapabilities") == 0) {
        result = tdom_soap_new("urn:schemas-upnp-org:service:ContentDirectory:1", "GetSortCapabilitiesResponse");
        tdom_soap_set(result, "SortCaps", "");
    } else if (strcmp(action, "GetProtocolInfo") == 0) {
        result = tdom_soap_new("urn:schemas-upnp-org:service:ConnectionManager:1", "GetProtocolInfoResponse");
        tdom_soap_set(result, "Source", "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3");
        tdom_soap_set(result, "Sink", "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3");
    } else if (strcmp(action, "Browse") == 0) {
        result = tdom_soap_new("urn:schemas-upnp-org:service:ContentDirectory:1", "BrowseResponse");
        tdom_soap_set(result, "Result",
                "<DIDL-Lite "
                "  xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
                "  xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"\n"
                "  xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\">\n"
                "<container id=\"0\">\n"
                "  <dc:title>dir</dc:title>\n"
                "  <upnp:class>object.container</upnp:class>\n"
                "</container>\n"
                "<container id=\"1\">\n"
                "  <dc:title>dir1</dc:title>\n"
                "  <upnp:class>object.container</upnp:class>\n"
                "</container>\n"
                "</DIDL-Lite>\n"
                );
        tdom_soap_set(result, "NumberReturned", "1");
        tdom_soap_set(result, "TotalMatches", "1");
        tdom_soap_set(result, "UpdateID", "1");
    } else {
        goto out;
    }

    tdom_writer_write_all(buffer, result);
    evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charset=\"utf-8\"");
    evhttp_send_reply(req, HTTP_OK, "OK", buffer);

    evbuffer_free(buffer);
    tdom_doc_delete(result);

    return;

out:
    evhttp_send_error(req, 500, "Internal Server Error");
}

static struct tdom_doc *load_doc(const char *filename)
{
    int fd;
    char buff[32];
    int len;
    struct tdom_parser *parser;
    struct tdom_doc *doc;

    if ((parser = tdom_parser_new()) == NULL)
        return NULL;

    if ((fd = open(filename, O_RDONLY, 0)) == -1)
        goto out;

    for (;;) {
        len = read(fd, buff, sizeof(buff));
        if (len <= 0)
            goto out;

        len = tdom_parser_parse(parser, buff, len);
        if (len == 1)
            break;
        else if (len < 0)
            goto out;
    }

    doc = tdom_parser_get_doc(parser);
    tdom_parser_delete(parser);

    return doc;

out:
    if (fd != -1)
        tansu_socket_close(fd);

    tdom_parser_delete(parser);
    return NULL;
}

#if 0
static void
dump_xml(struct tdom_doc *doc)
{
    struct tdom_writer *writer;
    struct evbuffer *buffer;

    buffer = evbuffer_new();
    writer = tdom_writer_new();
    tdom_writer_set(writer, doc);
    tdom_writer_write_all(writer, buffer);

    printf("%s\n", buffer->buffer);
    evbuffer_free(buffer);
}
#endif

static struct tansu_device *
create_ms(const char *udn, const char *ms_path, const char *cds_path, const char *cms_path)
{
    unsigned int i;
    struct tdom_doc *doc;
    struct tansu_device *device;
    tansu_status_t status;
    struct {
        const char *path;
        const char *type;
        const char *sid;
        void (*cb)(struct evhttp_request *req,
                   struct tansu_device *device,
                   struct tansu_service *service,
                   const char *action,
                   void *arg);
        void *cbarg;
    } spec[] = {
        {
            cds_path,
            "urn:schemas-upnp-org:service:ContentDirectory:1",
            "urn:upnp-org:serviceId:ContentDirectory",
            tansu_device_entry_soap_exec, NULL,
        },
        {
            cms_path,
            "urn:schemas-upnp-org:service:ConnectionManager:1",
            "urn:upnp-org:serviceId:ConnectionManager",
            tansu_device_entry_soap_exec, NULL,
        },
    };

    if ((doc = load_doc(ms_path)) == NULL) {
        return NULL;
    }
    if ((device = tansu_device_new(doc, udn)) == NULL) {
        goto out;
    }
    doc = NULL;

    for (i = 0; i < sizeof(spec)/sizeof(*spec); i++) {
        if ((doc = load_doc(spec[i].path)) == NULL) {
            goto out;
        }
        status = tansu_device_add_service(device, spec[i].type, spec[i].sid, doc, spec[i].cb, spec[i].cbarg);
        if (status != TANSU_SUCCESS) {
            goto out;
        }
    }

    return device;

out:
    tdom_doc_delete(doc);
    tansu_device_delete(device);
    return NULL;
}

static struct tansu_device *
create_mr(const char *udn, const char *mr_path, const char *avt_path, const char *cms_path, const char *rcs_path)
{
    unsigned int i;
    struct tdom_doc *doc;
    struct tansu_device *device;
    tansu_status_t status;
    struct {
        const char *path;
        const char *type;
        const char *sid;
        void (*cb)(struct evhttp_request *req,
                   struct tansu_device *device,
                   struct tansu_service *service,
                   const char *action,
                   void *arg);
        void *cbarg;
    } spec[] = {
        {
            avt_path,
            "urn:schemas-upnp-org:service:AVTransport:1",
            "urn:upnp-org:serviceId:AVTransport",
            tansu_device_entry_soap_exec, NULL,
        },
        {
            cms_path,
            "urn:schemas-upnp-org:service:ConnectionManager:1",
            "urn:upnp-org:serviceId:ConnectionManager",
            tansu_device_entry_soap_exec, NULL,
        },
        {
            rcs_path,
            "urn:schemas-upnp-org:service:RenderingControl:1",
            "urn:upnp-org:serviceId:RenderingControl",
            tansu_device_entry_soap_exec, NULL,
        },
    };

    if ((doc = load_doc(mr_path)) == NULL) {
        return NULL;
    }
    if ((device = tansu_device_new(doc, udn)) == NULL) {
        goto out;
    }
    doc = NULL;

    for (i = 0; i < sizeof(spec)/sizeof(*spec); i++) {
        if ((doc = load_doc(spec[i].path)) == NULL) {
            goto out;
        }
        status = tansu_device_add_service(device, spec[i].type, spec[i].sid, doc, spec[i].cb, spec[i].cbarg);
        if (status != TANSU_SUCCESS) {
            goto out;
        }
    }

    return device;

out:
    tdom_doc_delete(doc);
    tansu_device_delete(device);
    return NULL;
}

int main(int argc, char *argv[])
{
    struct event_base *evbase;
    struct tansu_upnp_base *base;
    struct tansu_device *ms = NULL;
    struct tansu_device *ms1 = NULL;
    struct tansu_device *ms2 = NULL;
    struct tansu_device *mr = NULL;
    struct tansu_nwifs *nwifs;

    if (argc < 2) {
        printf("usage: %s DD [SCPD...]\n", argv[0]);
        exit(1);
    }

    if (argc == 4) {
        ms = create_ms("uuid:00000000-0000-1010-8000-001122334455", argv[1], argv[2], argv[3]);
        if (ms == NULL) {
            printf("create_ms() failed\n");
            return -1;
        }
    } else if (argc == 5) {
        mr = create_mr("uuid:00010000-0000-1010-8000-001122334455", argv[1], argv[2], argv[3], argv[4]);
        if (mr == NULL) {
            printf("create_mr() failed\n");
            return -1;
        }
    } else if (argc == 8) {
        ms = create_ms("uuid:00000000-0000-1010-8000-001122334455", argv[1], argv[2], argv[3]);
        ms1 = create_ms("uuid:00000001-0000-1010-8000-001122334455", argv[1], argv[2], argv[3]);
        ms2 = create_ms("uuid:00000002-0000-1010-8000-001122334455", argv[1], argv[2], argv[3]);
        mr = create_mr("uuid:00010000-0000-1010-8000-001122334455", argv[4], argv[5], argv[6], argv[7]);
        if (ms == NULL || mr == NULL) {
            return -1;
        }
    }

    evbase = event_base_new();

    base = tansu_upnp_base_new();
    TASSERT(base);
    base->server_name = strdup("Linux/2.6, UPnP/1.0, noname/0.1");
    TASSERT(base->server_name);

    nwifs = tansu_nwif_getaddrs(NULL);
    TASSERT(nwifs);
    tansu_upnp_base_start(evbase, base, 18000, nwifs);
    tansu_nwif_freeaddrs(nwifs);

    if (ms) {
        tansu_upnp_base_add_device(base, ms);
    }
    if (ms1) {
        tansu_upnp_base_add_device(base, ms1);
    }
    if (ms2) {
        tansu_upnp_base_add_device(base, ms2);
    }
    if (mr) {
        tansu_upnp_base_add_device(base, mr);
    }

    event_base_dispatch(evbase);
    event_base_free(evbase);

    return 0;
}

